package cn.sisyphe.framework.monitor.runtime;

import net.logstash.logback.marker.LogstashMarker;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import static net.logstash.logback.marker.Markers.append;

/**
 * Created by heyong on 2017/11/1 10:36
 * Description:
 *
 * @author heyong
 */
@Aspect
@Component
public class MethodMonitor {

    private static final Logger log = LoggerFactory.getLogger(MethodMonitor.class);

    @Pointcut("execution(* cn.sisyphe..*Controller.*(..))")
    public void controller() {
    }

    @Pointcut("execution(* cn.sisyphe..*Repository*.*(..))")
    public void jpa() {
    }


    @Value("${sisyphe.monitor.controller.enable:false}")
    public boolean controllerEnable;

    @Value("${sisyphe.monitor.jpa.enable:false}")
    public boolean jpaEnable;

    @Value("${spring.application.name:app}")
    public String applicationName;

    /**
     * controller 监控
     * @param thisJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("controller()")
    public Object invoke(ProceedingJoinPoint thisJoinPoint) throws Throwable {

        if (!controllerEnable) {
            return thisJoinPoint.proceed();
        }

        MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
        String name = methodSignature.toString();


        MethodRunTime methodRunTime = new MethodRunTime(name, MonitorTypeEnum.CONTROLLER, applicationName);

        StopWatch stopWatch = new StopWatch(name);
        stopWatch.start();
        Object object = thisJoinPoint.proceed();
        stopWatch.stop();

        methodRunTime.setTotalTime(stopWatch.getTotalTimeMillis());

        writeLog(methodRunTime);

        return object;
    }


    /**
     * jpa 监控
     * @param thisJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("jpa()")
    public Object invokeJpa(ProceedingJoinPoint thisJoinPoint) throws Throwable {

        if (!jpaEnable) {
            return thisJoinPoint.proceed();
        }

        MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
        String name = methodSignature.toString();


        MethodRunTime methodRunTime = new MethodRunTime(name, MonitorTypeEnum.JPA, applicationName);

        StopWatch stopWatch = new StopWatch(name);
        stopWatch.start();
        Object object = thisJoinPoint.proceed();
        stopWatch.stop();

        methodRunTime.setTotalTime(stopWatch.getTotalTimeMillis());

        writeLog(methodRunTime);

        return object;
    }

    /**
     * 写日志，可由kafka收集
     * @param methodRunTime
     */
    private void writeLog(MethodRunTime methodRunTime) {
        Field[] fields = methodRunTime.getClass().getDeclaredFields();
        LogstashMarker logstashMarker = append("monitor", "runtime");

        for (Field field : fields) {
            logstashMarker.and(append(field.getName(), getFieldValue(methodRunTime, field.getName())));
        }

        log.info(logstashMarker, methodRunTime.toString());
    }

    /**
     * 获取属性值
     * @param thisClass
     * @param fieldName
     * @return
     */
    private Object getFieldValue(Object thisClass, String fieldName) {
        Object value = new Object();
        Method method;
        try {
            String methodName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            method = thisClass.getClass().getMethod("get" + methodName);
            value = method.invoke(thisClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }
}
